/************************************
 * Package: RGcpp					*
 * Authors: Rui Eduardo Gouveia Gil	*
 * Date: 2010						*
 ************************************/
#include "rgcpp/net/sockets/UnixServerSocket.h"
#ifndef _WIN32
#include <sys/stat.h>

namespace rgcpp
{

UnixServerSocket::UnixServerSocket(std::string socketPath) : socketAccept(-1), socketPath(socketPath)
{
	this->releaseForced=false;
	this->depth=DEFAULT_SERVER_SOCKET_DEPTH;

	if((socketAccept = socket(AF_UNIX,AF_UNIX,0)) == -1)
	{
		Throw(SocketException(std::string("Error creating the unixsocket - ")+strerror(errno)));
	}

	struct sockaddr_un addr;
	memset(&addr, 0, sizeof(addr));

	addr.sun_family = AF_UNIX;
	strcpy(addr.sun_path,socketPath.c_str());
	unlink(addr.sun_path);

	//Open the socket
	if(::bind(socketAccept,(struct sockaddr *)&addr,sizeof(addr)) == -1)
	{
		shutdown(socketAccept,SHUT_RDWR);
		close(socketAccept);

		Throw(SocketException(std::string("Error creating the unixsocket - ")+strerror(errno)));
	}

	// Set a limit on connection queue.
	if(::listen(socketAccept, this->depth) != 0)
	{
		shutdown(socketAccept,SHUT_RDWR);
		close(socketAccept);

		Throw(SocketException(std::string("Error creating the unixsocket - ")+strerror(errno)));
	}
}

UnixServerSocket::UnixServerSocket(std::string socketPath, std::string user, std::string group) : socketAccept(-1), socketPath(socketPath)
{
	this->releaseForced=false;
	this->depth=DEFAULT_SERVER_SOCKET_DEPTH;

	if((socketAccept = socket(AF_UNIX,AF_UNIX,0)) == -1)
	{
		Throw(SocketException(std::string("Error creating the unixsocket - ")+strerror(errno)));
	}

	struct sockaddr_un addr;
	memset(&addr, 0, sizeof(addr));

	addr.sun_family = AF_UNIX;
	strcpy(addr.sun_path,socketPath.c_str());
	unlink(addr.sun_path);

	//Open the socket
	if(::bind(socketAccept,(struct sockaddr *)&addr,sizeof(addr)) == -1)
	{
		shutdown(socketAccept,SHUT_RDWR);
		close(socketAccept);

		Throw(SocketException(std::string("Error creating the unixsocket - ")+strerror(errno)));
	}

	// Set a limit on connection queue.
	if(::listen(socketAccept, this->depth) != 0)
	{
		shutdown(socketAccept,SHUT_RDWR);
		close(socketAccept);

		Throw(SocketException(std::string("Error creating the unixsocket - ")+strerror(errno)));
	}

	::chmod(socketPath.c_str(),(S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP));
	if(::chown(socketPath.c_str(),this->getUserId(user),this->getGroupId(group))== -1)
	{
		Throw(SocketException(std::string("Error creating the unixsocket - ")+strerror(errno)));
	}
}

UnixServerSocket::~UnixServerSocket()
{
	boost::mutex::scoped_lock lock(mut);

	if(socketAccept!=-1)
	{
		shutdown(socketAccept,SHUT_RDWR);
		close(socketAccept);
	}
}

UnixSocket* UnixServerSocket::accept()
{
	boost::mutex::scoped_lock lock(mut);

	if(this->releaseForced)
		return NULL;

	// Open up new connection
	struct sockaddr_un addr;
	int len = sizeof(addr);

	int client = ::accept(socketAccept,(struct sockaddr *)&addr,(socklen_t *)&len);

	if(this->releaseForced)
		return NULL;

	if(client == -1)
	{
		Throw(SocketException(std::string("Error server unixsocket - ")+strerror(errno)));
	}

	return new UnixSocket(client);
}

void UnixServerSocket::release()
{
	this->releaseForced=true;

	::shutdown(socketAccept,SHUT_RDWR);
}

void UnixServerSocket::setDepth(unsigned int depth)
{
	this->depth=depth;

	// Set a limit on connection queue.
	if(::listen(socketAccept, this->depth) != 0)
	{
		Throw(SocketException(std::string("Unable to set limit on connection queue")+strerror(errno)));
	}
}

unsigned int UnixServerSocket::getDepth()
{
	return this->depth;
}

int UnixServerSocket::getUserId(std::string& user)
{
	std::ifstream stream("/etc/passwd",std::ios_base::in);

	if(!stream.good())
	{
		Throw(ConfigException("Invalid passwd file."));
	}

	size_t pos;
	std::string line;
	while(!stream.eof())
	{
		getline(stream,line);
		if(!line.empty())
		{
			pos = line.find(':');
			if(line.substr(0,pos).compare(user) == 0)
			{
				line = line.substr(pos+1); //remove user

				pos = line.find(':');
				line = line.substr(pos+1); //remove x

				pos = line.find(':');

				stream.close();
				return atoi(line.substr(0,pos).c_str());
			}
		}
	}

	stream.close();
	Throw(ConfigException("Invalid passwd file - user not found."));
}

int UnixServerSocket::getGroupId(std::string& group)
{
	std::ifstream stream("/etc/group",std::ios_base::in);

	if(!stream.good())
	{
		Throw(ConfigException("Invalid group file."));
	}

	size_t pos;
	std::string line;
	while(!stream.eof())
	{
		getline(stream,line);
		if(!line.empty())
		{
			pos = line.find(':');
			if(line.substr(0,pos).compare(group) == 0)
			{
				line = line.substr(pos+1); //remove group

				pos = line.find(':');
				line = line.substr(pos+1); //remove x

				pos = line.find(':');

				stream.close();
				return atoi(line.substr(0,pos).c_str());
			}
		}
	}

	stream.close();
	Throw(ConfigException("Invalid group file - group not found."));
}

}
#endif